package com.dreamstations.changshads.util;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;


import java.io.File;
import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.util.List;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import static com.dreamstations.changshads.util.DSOfflineDataProvider.DEV_OFFLINE;

/**
 * Created by QYH on 2016/6/2.
 */

public class DSUtils {
    private static final String TAG = DSUtils.class.getName();
    private static final long SIZE_OF_CACHE = 10 * 1024 * 1024; //10MB
    private static final long MAXTIME_OF_CACHE = 3600; //1小时


    public static final int ONLY_NETWORK = 0;
    public static final int ONLY_CACHED = 1;
    public static final int CACHED_ELSE_NETWORK = 2;
    public static final int NETWORK_ELSE_CACHED = 3;

    private static Cache cache;
    private static DSUtils mInstance;

    private OkHttpClient okHttpClient;
    private Handler mDelivery;

    private DSUtils() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
//        okHttpClient = new OkHttpClient();
//        okHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER));

        //拦截Response以启用缓存
        builder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response originalResponse = chain.proceed(chain.request());
                return originalResponse.newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        .header("Cache-Control", String.format("max-age=%d", MAXTIME_OF_CACHE))
                        .build();
            }
        });
        if (cache != null) {
//            okHttpClient.setCache(cache);
            builder.cache(cache);
            L.i(TAG, "=>DSUtils:cache enabled!");
        }
        okHttpClient = builder.build();
        mDelivery = new Handler(Looper.getMainLooper());
    }

    /**
     * 在Application中初始化缓存
     *
     * @param context
     */
    public static void InitCache(Context context) {
        File cacheDirectory = new File(context.getExternalCacheDir(), "HttpCache");
        cache = new Cache(cacheDirectory, SIZE_OF_CACHE);
    }

    public static DSUtils getInstance() {
        if (mInstance == null) {
            synchronized (DSUtils.class) {
                mInstance = new DSUtils();
            }
        }
        return mInstance;
    }

    public DSRequest get() {
        return new DSRequest();
    }

    public void execute(final DSRequest dsRequest, final DSRequest.DSCallback dsCallback) {
        if (dsCallback == null) return;
        //直接连通前后端的接口
        final Callback callback = new Callback() {
            @Override
            public void onFailure(Call call, final IOException e) {
                mDelivery.post(new Runnable() {
                    @Override
                    public void run() {
                        dsCallback.onFailure(e.getMessage());
                    }
                });
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.code() != 200) {
                    onFailure(null, new IOException("请求失败!"));
                    return;
                }
                final Object o;
                try {
                    o = DSParser.parse(dsRequest.getType(), response.body().string());
                    if (o == null) throw new Exception("解析结果为空");
                    mDelivery.post(new Runnable() {
                        @Override
                        public void run() {
                            dsCallback.onResponse(o);
                        }
                    });
                } catch (Exception e) {
                    onFailure(null, new IOException("Parse failed!"));
                    e.printStackTrace();
                }
            }
        };

        //在离线版只访问存好的数据
        if (DEV_OFFLINE) {
            L.i(TAG, "=>execute:" + "OFFLINE MODE");
            L.i(TAG, "=>execute:" + dsRequest.getUrl());
            final Object o;
            try {
                o = DSOfflineDataProvider.getOfflineData(dsRequest.getType());
                if (o == null) throw new Exception("解析结果为空");
                mDelivery.post(new Runnable() {
                    @Override
                    public void run() {
                        dsCallback.onResponse(o);
                    }
                });
            } catch (Exception e) {
                e.printStackTrace();
                mDelivery.post(new Runnable() {
                    @Override
                    public void run() {
                        dsCallback.onFailure("获取离线数据遇到错误");
                    }
                });
            }
            return;
        }

        L.i(TAG, "=>execute:" + "ONLINE MODE," + dsRequest.getCacheType());
        //构造真正的请求
        final Request request = new Request.Builder()
                .url(dsRequest.getUrl())
                .tag(dsRequest.getTag())
                .build();
        L.i(TAG, "=>execute:" + dsRequest.getUrl());
        request(request
                , dsRequest.getCacheType()
                , callback);
    }


    private void requestFromCache(Request originRequest, Callback callback) {
        Request request = originRequest.newBuilder()
                .cacheControl(CacheControl.FORCE_CACHE)
                .build();
        if (callback != null) {
            okHttpClient.newCall(request).enqueue(callback);
        }

    }

    private void requestFromNetwork(Request originRequest, final Callback callback) {
        Request request = originRequest.newBuilder()
                .cacheControl(CacheControl.FORCE_NETWORK)
                .build();
        if (callback != null) {
            okHttpClient.newCall(request).enqueue(callback);
        }
    }

    /**
     * @param request
     * @param cacheType
     * @param callback  用于框架与前台的连接
     */
    public void request(final Request request, final int cacheType, @NonNull final Callback callback) {
        if (callback == null) return;
        switch (cacheType) {
            case ONLY_NETWORK:
                requestFromNetwork(request, callback);
                break;
            case ONLY_CACHED:
                requestFromCache(request, callback);
                break;
            //这种情况需要再加一层回调
            case NETWORK_ELSE_CACHED:
                requestFromNetwork(request, new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        L.i(TAG, "=>request:network failed,requesting from cache...");
                        requestFromCache(request, callback);
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        if (response.code() == 200) {
                            callback.onResponse(call, response);
                        } else {
                            requestFromCache(request, callback);
                        }
                    }
                });
                break;
            //这种情况需要再加一层回调
            case CACHED_ELSE_NETWORK:
                requestFromCache(request, new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        L.i(TAG, "=>request:cache failed,requesting from network...");
                        requestFromNetwork(request, callback);
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {
                        if (response.code() == 200) {
                            callback.onResponse(call, response);
                        } else {
                            requestFromNetwork(request, callback);
                        }
                    }
                });
                break;
        }
    }

    public void cancelByTag(Object tag) {
        for (Call call : okHttpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
        for (Call call : okHttpClient.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag()))
                call.cancel();
        }
    }
}
